/*
 * easysmtp - A simple C lib in Linux to help you send mails in an easy way
 * Copyright (C) 2012-2013, easysmtp developers and contributors
 *
 * easysmtp is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * easysmtp is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with easysmtp; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "process_chain.h"

static int connect_nonb(struct sockaddr* sp, 
		socklen_t slen, int nsec, SMTP *smtpr);
static int hello(SMTP *smtpr, MAIL *mpr);
static int auth(SMTP *smtpr, MAIL *mpr);
static int login(SMTP *smtpr, MAIL *mpr);
static int from_to(SMTP *smtpr, MAIL *mpr);
static int data(SMTP *smtpr, MAIL *mpr); 
static int mail_head(SMTP *smtpr, MAIL *mpr);
static int mail_text(SMTP *smtpr, MAIL *mpr);
static int deal_text(SMTP *smtpr, char *text, char *content_type );
static int mail_end(SMTP *smtpr, MAIL *mpr);
static int quit(SMTP *smtpr, MAIL *mpr);

//连接前准备
int
pre_connect(SMTP *smtpr, struct sockaddr_in *server, int port) {
	struct hostent *hptr;

	//申请套接字
	smtpr->sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(smtpr->sockfd == -1) {
		return SYS_ERROR;
	}

	//填充server结构
	server->sin_family = AF_INET;
	server->sin_port = htons(port);
	//判断使用者是使用了地址还是域名
	if(!smtpr->domain_flag) {
		if((hptr = gethostbyname(smtpr->server_addr)) != NULL) {
			memcpy(&server->sin_addr, hptr->h_addr_list[0], 
					hptr->h_length);
		}
		else {
			return SYS_ERROR;
		}
	}
	else {
		server->sin_addr.s_addr = inet_addr(smtpr->domain);
	}
	return SMTP_SUCCESS;
}

//连接
int 
connect_nonb(struct sockaddr* sp, 
		socklen_t slen, int nsec, SMTP *smtpr)  {
	int flags;
	int error = 0;
	int n;
	//分别定义一组读的，一组写的文件描述符集
	fd_set rset, wset; 
	socklen_t len;
	struct timeval tval;

	//取出当前套接字状态
	flags = fcntl(smtpr->sockfd, F_GETFL, 0);
	fcntl(smtpr->sockfd, F_SETFL, flags | O_NONBLOCK);

	if(( n = connect(smtpr->sockfd, sp, slen ))< 0) {
		//EINPROGRESS表示连接启动但尚未建立
		if(errno != EINPROGRESS) {
			return SYS_ERROR;
		}
	}
	//连接已建立
	if(n == 0)
	{
		fcntl(smtpr->sockfd, F_SETFL, flags);
		return SMTP_SUCCESS;
	}

	FD_ZERO(&rset);
	FD_SET(smtpr->sockfd, &rset);
	wset = rset;

	tval.tv_sec = nsec;
	tval.tv_usec = 0;
	//是否发生超时
	if(select(smtpr->sockfd+1, &rset, &wset, NULL,
			nsec? &tval: NULL) == 0) {
		close(smtpr->sockfd);
		//无法在规定时间建立连接
		errno = ETIMEDOUT;
		return SYS_ERROR;
	}
	//检测描述符变为可读或者可写
	if(FD_ISSET(smtpr->sockfd, &rset) 
			|| FD_ISSET(smtpr->sockfd, &wset)) {
		len = sizeof(error);
		/*getsockopt取得套接字的待处理错误
		 *如果为0，连接成功建立
		 */
		if(getsockopt(smtpr->sockfd, SOL_SOCKET, SO_ERROR, &error,
				&len) < 0) {
			return SYS_ERROR;
		}
	}
	else {
		return SYS_ERROR;
	}
	//恢复套接字状态
	flags = fcntl(smtpr->sockfd, F_SETFL, flags);
	return SMTP_SUCCESS;
}

//建立连接（调用连接的函数）
int 
smtp_connect(struct sockaddr *sp, socklen_t slen,
		int nsec, SMTP *smtpr) {
	if(SYS_ERROR == connect_nonb(sp, slen, nsec, smtpr)) {
		return SYS_ERROR;
	}
	return read_msg(smtpr);
}

/*
 * 连接已建立，开始一系列和SMTP服务器对话
 * EHLO向SMTP服务器标记自己的身份
 */
int 
hello(SMTP *smtpr, MAIL *mpr) {
	char temp[ANSWER_MAXLEN+1] = {0};
	int return_num;

	sprintf(temp, "%s %s\r\n", "EHLO", smtpr->username);
	if((return_num = send_msg(smtpr,temp)) != SMTP_SUCCESS) {
		return return_num;
	}
	return read_msg(smtpr);
}

//向SMTP服务器提供认证
int 
auth(SMTP *smtpr, MAIL *mpr) {
	char temp[ANSWER_MAXLEN+1] = {0};
	int return_num;
	
	/*
	 * 采取AUTH PLAIN认证
	 * 格式为"AUTH PLAIN base64(\0username\0passwd)"
	 */
	if(1 == smtpr->method_flag) {
		temp[0] = '\0';
		strcpy(&temp[1], smtpr->username);
		temp[strlen(smtpr->username)+1] = '\0';
		strcat(&temp[strlen(smtpr->username)+2], smtpr->passwd);
		cto64(temp, strlen(smtpr->username)+
				strlen(smtpr->passwd)+2);
		sprintf(temp, "%s %s\r\n", "AUTH PLAIN", change);
	}
	//采用AUTH LOGIN认证
	else {
		sprintf(temp, "%s\r\n", "AUTH LOGIN"); 
	}

	if((return_num = send_msg(smtpr,temp)) != SMTP_SUCCESS) {
		return return_num;
	}
	return read_msg(smtpr);
}

//登录（采用AUTH LOGIN才需要这个步骤）
int
login(SMTP *smtpr, MAIL *mpr) {
	int return_num;

	//对用户名编码，发送，读取
	cto64(smtpr->username, strlen(smtpr->username));
	sprintf(change, "%s\r\n", change);

	if((return_num = send_msg(smtpr,change)) != SMTP_SUCCESS) {
		return return_num;
	}
	if((return_num = read_msg(smtpr)) != SMTP_SUCCESS) {
		return return_num;
	}

	//对密码编码，发送，读取
	cto64(smtpr->passwd, strlen(smtpr->passwd));
	sprintf(change, "%s\r\n", change);

	if((return_num = send_msg(smtpr,change)) != SMTP_SUCCESS) {
		return return_num;
	}
	return read_msg(smtpr);
}

//告诉服务器收件人和发送人
int 
from_to(SMTP *smtpr, MAIL *mpr) {
	char temp[ANSWER_MAXLEN+1] = {0};
	int return_num, i;

	sprintf(temp, "MAIL FROM:<%s>\r\n", 
			smtpr->username);
	if((return_num = send_msg(smtpr,temp)) != SMTP_SUCCESS) {
		return return_num;
	}
	if((return_num = read_msg(smtpr))!= SMTP_SUCCESS) {
		return return_num;
	}

	//收件人可能有多个（收件人，抄送，密送都用RCPT TO）
	for(i = 0; i < smtpr->recv_len; i++) {
		sprintf(temp, "RCPT TO:<%s>\r\n", smtpr->recv_list[i]);
		if((return_num = send_msg(smtpr, temp)) != SMTP_SUCCESS) {
			return return_num;
		}
		if((return_num = read_msg(smtpr)) != SMTP_SUCCESS) {
			return return_num;
		}
	}
	for(i = 0; i < smtpr->cc_len; i++) {
		sprintf(temp, "RCPT TO:<%s>\r\n", smtpr->cc_list[i]);
		if((return_num = send_msg(smtpr, temp)) != SMTP_SUCCESS) {
			return return_num;
		}
		if((return_num = read_msg(smtpr)) != SMTP_SUCCESS) {
			return return_num;
		}
	}
	for(i = 0; i < smtpr->bcc_len; i++) {
		sprintf(temp, "RCPT TO:<%s>\r\n", smtpr->bcc_list[i]);
		if((return_num = send_msg(smtpr, temp)) != SMTP_SUCCESS) {
			return return_num;
		}
		if((return_num = read_msg(smtpr)) != SMTP_SUCCESS) {
			return return_num;
		}
	}
	return SMTP_SUCCESS;
}

//表示传输邮件信息开始
int 
data(SMTP *smtpr, MAIL *mpr) {
	char temp[ANSWER_MAXLEN+1] = {0};
	int return_num;

	sprintf(temp, "%s\r\n", "DATA");
	if((return_num = send_msg(smtpr,temp)) != SMTP_SUCCESS) {
		return return_num;
	}
	return read_msg(smtpr);
}

//构造信头并发送
int
mail_head(SMTP *smtpr, MAIL *mpr) {
	char *temp = (char *)malloc((smtpr->recv_len+smtpr->cc_len+
			smtpr->bcc_len)*2005);
	int i, return_num;

	//收件和发件人
	sprintf(temp, "From: %s\r\nTo: ", smtpr->username);
	for(i = 0; i < smtpr->recv_len; i++) {
		if(i != smtpr->recv_len-1) {
			sprintf(temp, "%s%s, ", temp, smtpr->recv_list[i]);
		}
		else {
			sprintf(temp, "%s%s\r\n", temp, 
					smtpr->recv_list[i]);	
		} 
	}

	//抄送
	if(smtpr->cc_len != 0) {
		sprintf(temp, "%sCc: ", temp);
	}
	for(i = 0; i < smtpr->cc_len; i++) {
		if(i != smtpr->cc_len-1) {
			sprintf(temp, "%s%s, ", temp, smtpr->cc_list[i]);
		}
		else {
			sprintf(temp, "%s%s\r\n", temp, 
					smtpr->cc_list[i]);	
		} 
	}

	//密送
	if(smtpr->bcc_len != 0) {
		sprintf(temp, "%sBcc: ", temp);
	}
	for(i = 0; i < smtpr->bcc_len; i++) {
		if(i != smtpr->bcc_len-1) {
			sprintf(temp, "%s%s, ", temp, smtpr->bcc_list[i]);
		}
		else {
			sprintf(temp, "%s%s\r\n", temp, 
					smtpr->bcc_list[i]);	
		} 
	}

	//一系列信头
	sprintf(temp, "%sSubject: %s\r\n"
			"Mime-Version: 1.0\r\n"
			"Content-Type: multipart/mixed;\r\n"
			"	boundary=\"#BOUNDARY1#\"\r\n\r\n\r\n"
			"This is multi-part message in MIME format.\r\n"
			"\r\n\r\n"
			"--#BOUNDARY1#\r\n"
			"Content-Type: multipart/alternative;\r\n"
			"	boundary=\"#BOUNDARY2#\"\r\n\r\n",
						temp, mpr->title);
	if((return_num = send_msg(smtpr,temp)) != SMTP_SUCCESS) {
		return return_num;
	}
	return SMTP_SUCCESS;
}

//邮件正文
int
mail_text(SMTP *smtpr, MAIL *mpr) {
	int return_num;

	//根据不同类型，不同参数调用正文处理函数
	if(mpr->text_plain_len) {
		if((return_num = deal_text(smtpr, mpr->text_plain, 
					"plain")) != SMTP_SUCCESS) {
			return return_num;
		}
	}
	if(mpr->text_html_len) {
		if((return_num = deal_text(smtpr, mpr->text_html,
					"html")) != SMTP_SUCCESS) {
			return return_num;
		}
	}
	//如果内容为空
	if(mpr->text_plain_len == 0 && mpr->text_html_len == 0) {
		if((return_num = deal_text(smtpr, "", 
					"plain")) != SMTP_SUCCESS) {
			return return_num;
		}
	}
	
	//如果有附件，调用附件处理函数
	if(mpr->att_len != 0) {
		return mail_attachment(smtpr, mpr);
	}
	return SMTP_SUCCESS;
}

//正文处理函数
int
deal_text(SMTP *smtpr, char *text, char *content_type ) {
	char *temp = (char *)malloc(strlen(text)*2+1000);
	char read_temp[TEXT_MAXLEN+1] = {0};
	int j = 0, return_num;

	sprintf(temp, "--#BOUNDARY2#\r\n"
			"Content-Type: text/%s; charset=UTF-8\r\n"
			"Content-Transfer-Encoding: base64\r\n",
					content_type);
	//每次100个字符发送(SMTP对每次发送字符有限制)
	while(j*TEXT_MAXLEN < strlen(text)) {
		strncpy(read_temp, &text[j*100], 100);
		ucto64((unsigned char *)read_temp, strlen(read_temp));
		sprintf(temp, "%s\r\n%s\r\n", temp, change);
		j++;
	}
	return_num = send_msg(smtpr, temp);

	free(temp);
	return return_num;
}

//邮件尾，以.结尾
int
mail_end(SMTP *smtpr, MAIL *mpr) {
	int return_num;

	if((return_num = send_msg(smtpr, "\r\n.\r\n")) != SMTP_SUCCESS) {
		return return_num;
	}
	return read_msg(smtpr);
}

//以“QUIT”结束本次对话
int
quit(SMTP *smtpr, MAIL *mpr) {
	char temp[ANSWER_MAXLEN+1] = {0};
	int return_num;

	sprintf(temp, "%s\r\n", "QUIT");
	if((return_num = send_msg(smtpr,temp)) != SMTP_SUCCESS) {
		return return_num;
	}
	return read_msg(smtpr);
}

//执行流程链
int
exec_process(SMTP *smtpr, MAIL *mpr) {
	int i;
	int num = (smtpr->method_flag == 1)? 
				AUTH_PLAIN_SIZE:AUTH_PLAIN_SIZE;
	PF process_chain[9] = {&hello, &auth, &login, &from_to, 
		&data, &mail_head, &mail_text, &mail_end, &quit};

	for(i = 0; i < num; i++) {
		//如果是AUTH PLAIN则不需要login步骤
		if(!smtpr->method_flag || i != 2) {
			int temp = process_chain[i](smtpr, mpr);
			if(temp != SMTP_SUCCESS) {
				printf("%d\n", i);
				return temp;
			}
		}
	}
	return SMTP_SUCCESS;
}
